#!/usr/bin/python2.7
# Copyright 2010 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Handler for Atom PFIF 1.2 person and note feeds."""

__author__ = 'kpy@google.com (Ka-Ping Yee)'

import atom
import config
import datetime
import model
import pfif
import utils

HARD_MAX_RESULTS = 200  # Clients can ask for more, but won't get more.
PERSON_SUBTITLE_BASE = "PFIF Person Feed generated by Person Finder at "
NOTE_SUBTITLE_BASE = "PFIF Note Feed generated by Person Finder at "

def get_latest_entry_date(entities):
    if entities:
        return max(entity.entry_date for entity in entities)
    return utils.get_utcnow()

def get_latest_repo_updated_date(repos):
    dates = [config.get_for_repo(repo, 'updated_date') for repo in repos]
    return dates and max(dates) or utils.get_utcnow()

def make_hidden_notes_blank(notes):
    for note in notes:
        if note.hidden:
            note.text = ''


class Repo(utils.BaseHandler):
    TITLE = 'Person Finder Repository Feed'

    repo_required = False
    https_required = False
    # For a deactivated repo, return an empty feed instead of an error page.
    ignore_deactivation = True

    def get(self):
        repos = model.Repo.list_launched()
        if self.repo:
            repos = [self.repo] if self.repo in repos else []

        self.response.headers['Content-Type'] = 'application/xml'
        atom.REPO_1_0.write_feed(
            self.response.out, repos, self.request.url, self.TITLE,
            get_latest_repo_updated_date(repos))
        utils.log_api_action(self, model.ApiActionLog.REPO)


class Person(utils.BaseHandler):
    https_required = True

    def get(self):
        if self.config.read_auth_key_required and not (
            self.auth and self.auth.read_permission):
            self.response.set_status(403)
            self.write('Missing or invalid authorization key\n')
            return

        pfif_version = self.params.version
        atom_version = atom.ATOM_PFIF_VERSIONS.get(pfif_version.version)

        max_results = min(self.params.max_results or 10, HARD_MAX_RESULTS)
        skip = self.params.skip or 0

        # We use a member because a var can't be modified inside the closure.
        self.num_notes = 0
        def get_notes_for_person(person):
            notes = model.Note.get_by_person_record_id(
                self.repo, person['person_record_id'])
            # Show hidden notes as blank in the Person feed (melwitt)
            # http://code.google.com/p/googlepersonfinder/issues/detail?id=58
            make_hidden_notes_blank(notes)

            records = map(pfif_version.note_to_dict, notes)
            utils.optionally_filter_sensitive_fields(records, self.auth)
            self.num_notes += len(notes)
            return records

        if self.params.omit_notes:  # Return only the person records.
            get_notes_for_person = lambda person: []

        query = model.Person.all_in_repo(self.repo, filter_expired=False)
        if self.params.min_entry_date:  # Scan forward.
            query = query.order('entry_date')
            query = query.filter('entry_date >=', self.params.min_entry_date)
        else:  # Show recent entries, scanning backward.
            query = query.order('-entry_date')

        persons = query.fetch(max_results, offset=skip)
        updated = get_latest_entry_date(persons)

        self.response.headers['Content-Type'] = 'application/xml'
        records = [pfif_version.person_to_dict(person, person.is_expired)
                   for person in persons]
        utils.optionally_filter_sensitive_fields(records, self.auth)
        atom_version.write_person_feed(
            self.response.out, records, get_notes_for_person,
            self.request.url, self.env.netloc, PERSON_SUBTITLE_BASE +
            self.env.netloc, updated)
        utils.log_api_action(self, model.ApiActionLog.READ, len(records),
                             self.num_notes)


class Note(utils.BaseHandler):
    # SSL check is done in get() if person_record_id is not specified.
    https_required = False

    def get(self):
        # SSL and auth key is not required if a feed for a specific person
        # is requested. Note that the feed icon on the person record page
        # links to HTTP version without auth key.
        if not self.params.person_record_id:
          # Check for SSL (unless running on localhost for development).
          if self.env.scheme != 'https' and self.env.domain != 'localhost':
              self.response.set_status(403)
              self.write('HTTPS is required.\n')
              return
          if self.config.read_auth_key_required and not (
              self.auth and self.auth.read_permission):
              self.response.set_status(403)
              self.write('Missing or invalid authorization key\n')
              return

        pfif_version = self.params.version
        atom_version = atom.ATOM_PFIF_VERSIONS.get(pfif_version.version)
        max_results = min(self.params.max_results or 10, HARD_MAX_RESULTS)
        skip = self.params.skip or 0

        query = model.Note.all_in_repo(self.repo)
        if self.params.min_entry_date:  # Scan forward.
            query = query.order('entry_date')
            query = query.filter('entry_date >=', self.params.min_entry_date)
        else:  # Show recent entries, scanning backward.
            query = query.order('-entry_date')

        if self.params.person_record_id:  # Show notes for a specific person.
            query = query.filter('person_record_id =',
                                 self.params.person_record_id)

        notes = query.fetch(max_results, offset=skip)
        updated = get_latest_entry_date(notes)

        # Show hidden notes as blank in the Note feed (melwitt)
        # http://code.google.com/p/googlepersonfinder/issues/detail?id=58
        make_hidden_notes_blank(notes)

        self.response.headers['Content-Type'] = 'application/xml'
        records = map(pfif_version.note_to_dict, notes)
        utils.optionally_filter_sensitive_fields(records, self.auth)
        atom_version.write_note_feed(
            self.response.out, records, self.request.url,
            self.env.netloc, NOTE_SUBTITLE_BASE + self.env.netloc, updated)
        utils.log_api_action(self, model.ApiActionLog.READ, 0, len(records))
